package com.dkd.service.impl;

import cn.elegent.token.dto.RefreshDTO;
import cn.elegent.token.dto.TokenDTO;
import cn.elegent.token.refresh.service.CustomService;
import cn.elegent.util.json.JsonUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * RefreshTokenServiceImpl
 *
 * @author: wgl
 * @describe: 刷新Token拓展实现策略
 * 需要实现每个刷新token只能使用一次
 * @date: 2022/12/28 10:10
 */
@ConditionalOnProperty(prefix = "elegent.token.custom",name = "check",havingValue = "one")
@Service
public class RefreshTokenServiceImpl implements CustomService {

    private final static String tokenKey = "refreshToken:";

    private final static String userIdKey = "userId";

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 用于校验刷新token
     * @param user
     * @return
     */
    @Override
    public Boolean checkRefreshToken(String refreshToken, Object user) {
        String redisKey = getRedisKey(user);
        //如果刷新token在redis中存在  并且当前redis中的刷新token就是传入的刷新token
        if((redisTemplate.opsForValue().get(redisKey)!=null)&&(refreshToken.equals(redisTemplate.opsForValue().get(redisKey)))){
            //表明本次请求刷新token是被允许的 将原有的redis的key删除
            redisTemplate.delete(redisKey);
            return true;
        }else{
            return false;
        }
    }

    /**
     * 记录刷新token到Redis中
     * @param accessToken
     * @param refreshToken
     */
    @Override
    public void customHook(TokenDTO accessToken, TokenDTO refreshToken,Object user) {
        String redisKey = getRedisKey(user);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        try {
            //计算相差多少秒
            Long secDiff = dateDiff(new Date(), sdf.parse(refreshToken.getLifespan()));
            redisTemplate.opsForValue().set(redisKey, refreshToken.getToken(), secDiff, TimeUnit.SECONDS);
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 在重新生成刷新token之后将刷新token存入redis中
     * @param refreshDTO
     * @param user
     */
    @Override
    public void afterRefreshHook(RefreshDTO refreshDTO, Object user) {
        String redisKey = getRedisKey(user);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        try {
            //计算相差多少秒
            Long secDiff = dateDiff(new Date(), sdf.parse(refreshDTO.getRefreshToken().getLifespan()));
            redisTemplate.opsForValue().set(redisKey, refreshDTO.getRefreshToken().getToken(), secDiff, TimeUnit.SECONDS);
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * 根据框架提供的用户对象获取redis里的key
     * @param user
     * @return
     */
    private String getRedisKey(Object user){
        String redisKey = null;
        try {
            Map<String, Object> payLoadMap = JsonUtil.convertToMap(user);
            redisKey = tokenKey + payLoadMap.get(userIdKey);
            return redisKey;
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }
    /**
     * 计算2个时间相差的天数、小时、分钟、秒
     * @param startTime 开始时间
     * @param endTime 截止时间
     * @return
     */
    public static Long dateDiff(Date startTime, Date endTime) {
        long nd = 1000 * 24 * 60 * 60;// 一天的毫秒数
        long nh = 1000 * 60 * 60;// 一小时的毫秒数
        long nm = 1000 * 60;// 一分钟的毫秒数
        long ns = 1000;// 一秒钟的毫秒数
        long diff;
        long second = 0;
        // 获得两个时间的毫秒时间差异
        try {
            diff = endTime.getTime() - startTime.getTime();
            // 计算差多少秒
            second = diff / ns;
            // 输出结果
            /*System.out.println("hour=" + hour + ",min=" + min);*/
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return second;
    }
}
